package com.summer.filesync.security;

import com.summer.filesync.util.Logger;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/** 认证服务类 管理用户认证、API密钥和访问控制 */
public class AuthenticationService {
  private static final Logger logger = Logger.getLogger(AuthenticationService.class);

  // 用户存储 - 在生产环境中应该使用数据库
  private final Map<String, User> users = new ConcurrentHashMap<>();

  // API密钥存储
  private final Map<String, ApiKey> apiKeys = new ConcurrentHashMap<>();

  // 活跃会话存储
  private final Map<String, Session> activeSessions = new ConcurrentHashMap<>();

  // 登录失败计数
  private final Map<String, FailureTracker> loginFailures = new ConcurrentHashMap<>();

  // 最大登录失败次数
  private static final int MAX_LOGIN_FAILURES = 5;

  // 账户锁定时间（分钟）
  private static final int LOCKOUT_DURATION_MINUTES = 30;

  private static AuthenticationService instance;

  private AuthenticationService() {
    initializeDefaultUsers();
  }

  public static synchronized AuthenticationService getInstance() {
    if (instance == null) {
      instance = new AuthenticationService();
    }
    return instance;
  }

  /** 初始化默认用户 */
  private void initializeDefaultUsers() {
    // 创建默认管理员用户
    String adminSalt = JwtUtil.generateSalt();
    String adminPasswordHash = JwtUtil.hashPassword("admin123", adminSalt);
    User admin =
        new User(
            "admin", adminPasswordHash, adminSalt, UserRole.ADMIN, "Default Administrator", true);
    users.put("admin", admin);

    // 创建默认观察者用户
    String viewerSalt = JwtUtil.generateSalt();
    String viewerPasswordHash = JwtUtil.hashPassword("viewer123", viewerSalt);
    User viewer =
        new User("viewer", viewerPasswordHash, viewerSalt, UserRole.VIEWER, "Default Viewer", true);
    users.put("viewer", viewer);

    // 创建默认API密钥
    String adminApiKey = JwtUtil.generateApiKey();
    ApiKey adminApiKeyObj =
        new ApiKey(adminApiKey, "admin", UserRole.ADMIN, "Default Admin API Key", true);
    apiKeys.put(adminApiKey, adminApiKeyObj);

    logger.info("默认用户初始化完成:");
    logger.info("管理员用户: admin / admin123");
    logger.info("观察者用户: viewer / viewer123");
    logger.info("管理员API密钥: " + adminApiKey);
  }

  /**
   * 用户名密码认证
   *
   * @param username 用户名
   * @param password 密码
   * @return 认证结果
   */
  public AuthenticationResult authenticate(String username, String password) {
    if (username == null || password == null) {
      return AuthenticationResult.failure("用户名或密码不能为空");
    }

    // 检查账户是否被锁定
    if (isAccountLocked(username)) {
      return AuthenticationResult.failure("账户已被锁定，请稍后再试");
    }

    User user = users.get(username);
    if (user == null || !user.isEnabled()) {
      recordLoginFailure(username);
      return AuthenticationResult.failure("用户名或密码错误");
    }

    // 验证密码
    String passwordHash = JwtUtil.hashPassword(password, user.getSalt());
    if (!passwordHash.equals(user.getPasswordHash())) {
      recordLoginFailure(username);
      return AuthenticationResult.failure("用户名或密码错误");
    }

    // 清除登录失败记录
    loginFailures.remove(username);

    // 生成JWT令牌
    String token = JwtUtil.generateToken(username, user.getRole());
    if (token == null) {
      return AuthenticationResult.failure("令牌生成失败");
    }

    // 创建会话
    Session session = new Session(username, user.getRole(), LocalDateTime.now());
    activeSessions.put(token, session);

    logger.info("用户 " + username + " 登录成功，角色: " + user.getRole());
    return AuthenticationResult.success(token, user);
  }

  /**
   * API密钥认证
   *
   * @param apiKey API密钥
   * @return 认证结果
   */
  public AuthenticationResult authenticateApiKey(String apiKey) {
    if (apiKey == null || apiKey.trim().isEmpty()) {
      return AuthenticationResult.failure("API密钥不能为空");
    }

    ApiKey key = apiKeys.get(apiKey);
    if (key == null || !key.isEnabled()) {
      return AuthenticationResult.failure("无效的API密钥");
    }

    // 更新最后使用时间
    key.updateLastUsed();

    // 创建临时用户对象用于返回
    User apiUser = new User(key.getOwner(), "", "", key.getRole(), key.getDescription(), true);

    logger.info("API密钥认证成功，所有者: " + key.getOwner() + "，角色: " + key.getRole());
    return AuthenticationResult.success(apiKey, apiUser);
  }

  /**
   * JWT令牌认证
   *
   * @param token JWT令牌
   * @return 认证结果
   */
  public AuthenticationResult authenticateToken(String token) {
    if (token == null || token.trim().isEmpty()) {
      return AuthenticationResult.failure("令牌不能为空");
    }

    // 验证JWT令牌
    TokenValidationResult validation = JwtUtil.validateToken(token);
    if (!validation.isValid()) {
      // 清除无效会话
      activeSessions.remove(token);
      return AuthenticationResult.failure(validation.getErrorMessage());
    }

    // 检查会话是否存在
    Session session = activeSessions.get(token);
    if (session == null) {
      return AuthenticationResult.failure("会话不存在或已过期");
    }

    // 更新会话最后访问时间
    session.updateLastAccess();

    // 获取用户信息
    User user = users.get(validation.getUsername());
    if (user == null || !user.isEnabled()) {
      activeSessions.remove(token);
      return AuthenticationResult.failure("用户不存在或已禁用");
    }

    return AuthenticationResult.success(token, user);
  }

  /**
   * 注销用户
   *
   * @param token JWT令牌
   */
  public void logout(String token) {
    Session session = activeSessions.remove(token);
    if (session != null) {
      logger.info("用户 " + session.getUsername() + " 已注销");
    }
  }

  /**
   * 检查权限
   *
   * @param token 认证令牌
   * @param requiredRole 所需角色
   * @return 是否有权限
   */
  public boolean hasPermission(String token, UserRole requiredRole) {
    AuthenticationResult result = authenticateToken(token);
    return result.isSuccess() && result.getUser().getRole().hasPermission(requiredRole);
  }

  /**
   * 检查权限级别
   *
   * @param token 认证令牌
   * @param requiredLevel 所需权限级别
   * @return 是否有权限
   */
  public boolean hasPermission(String token, int requiredLevel) {
    AuthenticationResult result = authenticateToken(token);
    return result.isSuccess() && result.getUser().getRole().hasPermission(requiredLevel);
  }

  /**
   * 创建新用户
   *
   * @param username 用户名
   * @param password 密码
   * @param role 角色
   * @param description 描述
   * @return 是否创建成功
   */
  public boolean createUser(String username, String password, UserRole role, String description) {
    if (users.containsKey(username)) {
      return false;
    }

    String salt = JwtUtil.generateSalt();
    String passwordHash = JwtUtil.hashPassword(password, salt);
    User user = new User(username, passwordHash, salt, role, description, true);
    users.put(username, user);

    logger.info("创建新用户: " + username + "，角色: " + role);
    return true;
  }

  /**
   * 创建API密钥
   *
   * @param owner 所有者
   * @param role 角色
   * @param description 描述
   * @return API密钥
   */
  public String createApiKey(String owner, UserRole role, String description) {
    String apiKey = JwtUtil.generateApiKey();
    ApiKey key = new ApiKey(apiKey, owner, role, description, true);
    apiKeys.put(apiKey, key);

    logger.info("为用户 " + owner + " 创建API密钥，角色: " + role);
    return apiKey;
  }

  /** 记录登录失败 */
  private void recordLoginFailure(String username) {
    FailureTracker tracker = loginFailures.computeIfAbsent(username, k -> new FailureTracker());
    tracker.recordFailure();

    if (tracker.getFailureCount() >= MAX_LOGIN_FAILURES) {
      logger.warn("用户 " + username + " 登录失败次数过多，账户已被锁定");
    }
  }

  /** 检查账户是否被锁定 */
  private boolean isAccountLocked(String username) {
    FailureTracker tracker = loginFailures.get(username);
    if (tracker == null) {
      return false;
    }

    if (tracker.getFailureCount() >= MAX_LOGIN_FAILURES) {
      LocalDateTime lockoutEnd =
          tracker.getFirstFailureTime().plusMinutes(LOCKOUT_DURATION_MINUTES);
      if (LocalDateTime.now().isBefore(lockoutEnd)) {
        return true;
      } else {
        // 锁定时间已过，清除失败记录
        loginFailures.remove(username);
        return false;
      }
    }

    return false;
  }

  /** 获取活跃会话数 */
  public int getActiveSessionCount() {
    return activeSessions.size();
  }

  /** 获取用户列表 */
  public Set<String> getUserList() {
    return new HashSet<>(users.keySet());
  }

  /** 清理过期会话 */
  public void cleanupExpiredSessions() {
    LocalDateTime cutoff = LocalDateTime.now().minusHours(24);
    activeSessions.entrySet().removeIf(entry -> entry.getValue().getLastAccess().isBefore(cutoff));
  }

  /** 登录失败跟踪器 */
  private static class FailureTracker {
    private int failureCount = 0;
    private LocalDateTime firstFailureTime;

    public void recordFailure() {
      if (failureCount == 0) {
        firstFailureTime = LocalDateTime.now();
      }
      failureCount++;
    }

    public int getFailureCount() {
      return failureCount;
    }

    public LocalDateTime getFirstFailureTime() {
      return firstFailureTime;
    }
  }
}
